package com.kgc.scd.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.kgc.scd.bean.User;
import com.kgc.scd.config.WechatAuthConfig;
import com.kgc.scd.contant.CommonConstant;
import com.kgc.scd.mapper.UserMapper;
import com.kgc.scd.service.LoginRegiservice;
import com.kgc.scd.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern;

/**
 * Created On : 2022/5/25.
 * <p>
 * Author : xzy
 * <p>
 * Description: 微信授权登录操作入口
 */
@Slf4j
@RestController
public class WechatAuthController {

    @Autowired
    private WechatAuthConfig wechatAuthConfig;

    @Autowired
    private LoginRegiservice loginRegiservice;

    @Autowired
    private RedisUtils redisUtils;
    @Autowired(required = false)
    private UserMapper userMapper;

    /**
     * @author : xzy
     * @date   : 2022/5/25
     * @param  : []
     * @return : com.kgc.scd.util.RequestResult<java.lang.String>
     * @description : 生成请求微信官方，获取code值的完整地址，用户通过扫描访问该地址，进行手动授权
     */
    @GetMapping("/wxauthCodeUrl")
    public RequestResult<String> generateWechatAuthCodeUrl(){

        // 官方实例：https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        // 注意：由于授权操作安全等级较高，所以在发起授权请求时，微信会对授权链接做正则强匹配校验，如果链接的参数顺序不对，授权页面将无法正常访问
        // scope为snsapi_userinfo-拉取用户信息请求示例：
        // https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxf0e81c3bee622d60&redirect_uri=http%3A%2F%2Fnba.bluewebgame.com%2Foauth_response.php&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect

        // 定义获取code值的请求地址拼接对象
        StringBuilder wxauthCodeUrl = new StringBuilder(wechatAuthConfig.getCodeUri());
        wxauthCodeUrl.append("?appid=").append(wechatAuthConfig.getAppId())
                .append("&redirect_uri=").append(wechatAuthConfig.getRedirectUri())
                .append("&response_type=code")
                .append("&scope=snsapi_userinfo")
                .append("&state=").append(UUID.randomUUID().toString().substring(0, 8))
                .append("#wechat_redirect");

        log.info("------ 1 用户同意授权，获取code，请求地址：{} ------", wxauthCodeUrl);

        // 接口返回一个完整的获取code请求地址，前端拿到地址生成二维码（手动模拟）
        return ResultBuildUtil.success(wxauthCodeUrl.toString());
    }

    /**
     * @author : xzy
     * @date   : 2022/5/25
     * @param  : [javax.servlet.http.HttpServletRequest]
     * @return : com.kgc.scd.util.RequestResult<java.util.Map<java.lang.String,java.lang.Object>>
     * @description : 接收微信官方异步回调请求，获取微信提供的code值 （不是上一步同步返回的，是异步请求回来的）
     *  流程：当上一步，用户访问获取code的请求地址，同意授权，微信官方会根据请求中提供的回调地址，进行异步回调
     */
    @RequestMapping("/wxauthCodeBack")
    public Map<String, Object> wechatAuthCodeBack(HttpServletRequest request){

        // 如果用户同意授权，页面将跳转至 redirect_uri?code=CODE&state=STATE。
        // code说明 ： code作为换取access_token的票据，每次用户授权带上的 code 将不一样，code只能使用一次，5分钟未被使用自动过期。
        // 从请求中，获取微信官方返回的code值
        String wxauthCode = request.getParameter("code");
        // 从请求中，获取微信官方返回的state值（第一步请求传过去）
        //String wxauthState = request.getParameter("state");
        log.info("------ 2 微信官方异步回调code结果：{} ------", wxauthCode);

        // 定义接口返回数据
        Map<String, Object> resultMap = new HashMap<>();
        //resultMap.put("code", wxauthCode);
        // 通过 code 换取网页授权access_token
        // 获取 code 后，请求以下链接获取access_token： https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        // 定义获取access_token值的请求地址拼接对象
        StringBuilder accessTokenUrl = new StringBuilder(wechatAuthConfig.getAccessTokenUri());
        accessTokenUrl.append("?appid=").append(wechatAuthConfig.getAppId())
                .append("&secret=").append(wechatAuthConfig.getAppSecret())
                .append("&code=").append(wxauthCode)
                .append("&grant_type=authorization_code");
        log.info("------ 3 通过微信官方返回的code，请求获取access_token的完整地址：{} ------", accessTokenUrl);

        // 通过微信官方返回的code，请求获取access_token，结果是同步返回的（不再是异步回调）
        // 要求：必须是程序中，根据上面的完整请求地址，发送请求到微信官方，获取结果，结果返回是一个json格式字符串，必须要进行结果解析
        // 程序中，发送http请求，到微信官方，获取结果
        String accessTokenJson = HttpClient4Util.getResponse4GetAsString(accessTokenUrl.toString(), "utf-8");

        // 程序中，解析微信官方返回的json字符串
        // 返回正确示例：{"access_token":"ACCESS_TOKEN","expires_in":7200,"refresh_token":"REFRESH_TOKEN","openid":"OPENID","scope":"SCOPE"}
        // 返回错误示例：{"errcode":40029,"errmsg":"invalid code"}
        JSONObject accessTokenJsonObject = JSON.parseObject(accessTokenJson);
        // 判断请求结果是否是正确的，如果是错误，直接结束
        if(!StringUtils.isEmpty(accessTokenJsonObject.getString("errcode"))){
            resultMap.put("errCode", accessTokenJsonObject.getString("errcode"));
            resultMap.put("errMsg", accessTokenJsonObject.getString("errmsg"));
            return resultMap;
        }
        // 获取拉取用户需要的参数：access_token	网页授权接口调用凭证，openid 用户唯一标识
        String accessToken = accessTokenJsonObject.getString("access_token");
        String openId = accessTokenJsonObject.getString("openid");

        // 拉取用户信息(需 scope 为 snsapi_userinfo)
        // 官方示例：http：GET（请使用 https 协议） https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
        // 定义获取用户详情的请求地址拼接对象
        StringBuilder userInfoUrl = new StringBuilder(wechatAuthConfig.getUserInfoUri());
        userInfoUrl.append("?access_token=").append(accessToken)
                .append("&openid=").append(openId)
                .append("&lang=zh_CN");
        log.info("------ 4、拉取用户信息(需 scope 为 snsapi_userinfo)，完整请求地址：{} ------", userInfoUrl);

        // 程序中，发送请求到微信官方，拉取用户信息，也是同步返回json字符串结果
        String userInfoResultJson = HttpClient4Util.getResponse4GetAsString(userInfoUrl.toString(), "utf-8");
        JSONObject userInfoResultJsonObject = JSON.parseObject(userInfoResultJson);
        String openid = userInfoResultJsonObject.getString("openid");
        // 根据openid判断该用户有没有用微信登陆过
        User user = loginRegiservice.haveAcct(openid);
        if(user != null){
            // 有账号 生成token存入redis
            String token = loginRegiservice.getTokenByWX(user);
            resultMap.put("msg","登陆成功");
            resultMap.put("vxid",openid);
            resultMap.put("token",token);
            return resultMap;
        }else{
            resultMap.put("msg","请绑定您的手机号");
            resultMap.put("vxid",openid);
            return resultMap;
        }
    }

    /**
    * @author: HarryXzy
    * @date: 2022/6/10 09:34
    * @return: com.kgc.scd.util.RequestResult<java.util.Map<java.lang.String,java.lang.Object>>
    * @description: 微信扫码登陆成功绑定手机号码
    */
    @GetMapping("/wxloginBindphone")
    public Map<String, String> wxloginBindphone(@RequestParam("phone") String phoneNO,
                                                @RequestParam("wxId") String wxId,
                                                @RequestParam("phoneCode") String phoneCode){

        HashMap<String, String> map = new HashMap<>();
        String pattern = "^1[3-9]\\d{9}$";
        if(!Pattern.matches(pattern, phoneNO)){
            map.put("code",CommonConstant.UNIFY_RETURN_USERPHONE_WRONG_CODE);
            map.put("msg","手机号格式错误");
            map.put("data","");
            return map;
        }
        // 判断该微信id是否已经绑定过
        if(loginRegiservice.selectWxid(wxId)){
            map.put("code",CommonConstant.UNIFY_RETURN_WX_HAVE_BIND_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_WX_HAVE_BIND_MSG);
            map.put("data","");
            return map;
        }
        // 手机获取验证码
        String rightCode = (String)redisUtils.get(phoneNO);
        if(!redisUtils.hasKey(phoneNO)){
            // 手机验证码失效
            map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_MSG);
            map.put("data","");
            return map;
        }
        else if(rightCode.equals(phoneCode)){

            // 系统中已有该手机号
            User user1 = loginRegiservice.havePhone(phoneNO);
            if(user1 != null){
                user1.setUserWxname(wxId);
                UpdateWrapper<User> wrapper = new UpdateWrapper<>();
                wrapper.eq("user_phone",phoneNO);
                userMapper.update(user1,wrapper);
                //map.put("code",CommonConstant.UNIFY_RETURN_PHONE_HAVE_BIND_CODE);
                //map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_HAVE_BIND_MSG);
                //map.put("data","");
                // 获取token并存入redis
                String token = loginRegiservice.getTokenByWX(user1);
                map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("data", token);
                return map;
            }

            // 验证码正确 从redis中删除验证码 并添加数据至数据库
            redisUtils.del(phoneNO);
            User user = User.builder().userWxname(wxId).userPhone(phoneNO).userId(generateUserID()).userName(generateUserName()).build();
            if(loginRegiservice.saveUser(user)){
                // 获取token并存入redis
                String token = loginRegiservice.getTokenByWX(user);
                map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("data",token);
                return map;
            }
            // 插入失败
            map.put("code",CommonConstant.UNIFY_RETURN_FAIL_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_FAIL_MSG);
            map.put("data","");
            return map;
        }
        else{
            // 手机验证码错误
            map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_MSG);
            map.put("data","");
            return map;
        }
    }


    /**
     * @author: HarryXzy
     * @date: 2022/6/8 15:32
     * @return: java.lang.String
     * @description: 生成用户ID
     */
    public String generateUserID(){
        // 生成用户ID的组成规则：系统标识+时间字符串+随机序列号
        return CommonConstant.USERID_PREFIX
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern(CommonConstant.TIME_PATTERN_MINUTE))
                + MyUtils.generateNonceStrUseUUID(4);
    }
    /**
     * @author: HarryXzy
     * @date: 2022/6/8 15:32
     * @return: java.lang.String
     * @description: 生成随机用户名
     */
    public String generateUserName(){
        // 生成用户ID的组成规则：系统标识+时间字符串+随机序列号
        return "N"
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern(CommonConstant.TIME_PATTERN_MINUTE))
                + MyUtils.generateNonceStrUseUUID(4);
    }

    /**
    * @author: HarryXzy
    * @date: 2022/6/10 09:35
    * @return:
    * @description: 获取手机验证码,每30秒获取一次
    */
    @GetMapping("/getPhoneCode")
    public Map<String, String> getPhoneCode(@RequestParam("phone") String phoneNo){
        HashMap<String, String> map = new HashMap<>();
        try {
            String pattern = "^1[3-9]\\d{9}$";
            if(!Pattern.matches(pattern, phoneNo)){
                map.put("code",CommonConstant.UNIFY_RETURN_USERPHONE_WRONG_CODE);
                map.put("msg","手机号格式错误");
                map.put("data","");
                return map;
            }
            if(redisUtils.hasKey(phoneNo + "DDL")){
                // 已获取验证码
                long expire = redisUtils.getExpire(phoneNo + "DDL");
                map.put("code",CommonConstant.UNIFY_RETURN_HAVE_GET_PHONE_CODE_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_HAVE_GET_PHONE_CODE_MSG+"请"+expire+"秒后重试");
                map.put("data", "");
                return map;
            }else{
                // 获取验证码，30s一次
                String code = UUID.randomUUID().toString().substring(0, 6);
                redisUtils.set(phoneNo,code,300);
                redisUtils.set(phoneNo + "DDL",code,30);
                map.put("code", CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("msg", CommonConstant.UNIFY_RETURN_SUCCESS_MSG);
                String s = "[百联商城】您的验证码为:"+code+"有效期为 5分钟，请确保是本人操作， 不要把验证码泄露给其他人。";
                map.put("data",s);
                return map;
            }
        }catch (Exception e){
            map.put("code", CommonConstant.UNIFY_RETURN_FAIL_CODE);
            map.put("msg", CommonConstant.UNIFY_RETURN_FAIL_MSG);
            map.put("data","");
            return map;
        }
    }


    /**
    * @author: HarryXzy
    * @date: 2022/6/10 11:30
    * @return: java.util.Map<java.lang.String,java.lang.String>
    * @description: 使用手机号登陆或注册
    */
    @PostMapping("/loginOrRigByPhone")
    public Map<String, String> loginOrRigByPhone(@RequestParam("phone") String phoneNo,
                                                 @RequestParam("phoneCode") String phoneCode){

        HashMap<String, String> map = new HashMap<>();
        // 手机获取验证码
        String rightCode = (String)redisUtils.get(phoneNo);
        if(!redisUtils.hasKey(phoneNo)){
            // 手机验证码失效
            map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_MSG);
            map.put("data","");
            return map;
        }
        else if(rightCode.equals(phoneCode)){
            // 验证码正确 从redis中删除验证码 并添加数据至数据库
            redisUtils.del(phoneNo);
            User user = loginRegiservice.getUserByPhone(phoneNo);
            if(user == null){
                // 注册用户
                user = User.builder().userPhone(phoneNo).userId(generateUserID()).userName(generateUserName()).build();
                loginRegiservice.saveUser(user);
            }
            // 获取token并存入redis
            String token = loginRegiservice.getTokenByWX(user);
            map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_MSG);
            map.put("data",token);
            return map;
        }
        else{
            // 手机验证码错误
            map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_MSG);
            map.put("data","");
            return map;
        }
    }


    /**
    * @author: HarryXzy
    * @date: 2022/6/10 12:30
    * @return: java.util.Map<java.lang.String,java.lang.String>
    * @description: 使用用户名或电话号 进行密码登陆
    */
    @PostMapping("/loginByUserNameAndPwd")
    public Map<String, String> loginByUserNameAndPwd(@RequestParam("userNameOrPhone")String userNameOrPhone,
                                                     @RequestParam("userPwd")String userPwd) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        String Pwd = MyUtils.MD5(userPwd);
        User user = loginRegiservice.loginByUsernameOrPhone(userNameOrPhone, Pwd);
        if(user != null){
            // 用户名或手机号 密码正确
            // 获取token并存入redis
            String token = loginRegiservice.getTokenByWX(user);
            map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_MSG);
            map.put("data",token);
            return map;
        }else{
            map.put("code",CommonConstant.UNIFY_RETURN_LOGIN_FAIL_CODE);
            map.put("msg",CommonConstant.UNIFY_RETURN_LOGIN_FAIL_MSG);
            map.put("data","" );
            return map;
        }
    }

    /**
    * @author: HarryXzy
    * @date: 2022/6/10 13:26
    * @return:
    * @description: 普通用户名密码注册
    */
    @PostMapping("/userRegister")
    public Map<String, String> userRegister(@RequestBody User user,
                                            @RequestParam("phoneCode")String phoneCode) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        try {
            // 判断绑定过的手机号不能再次绑定
            User user1 = loginRegiservice.havePhone(user.getUserPhone());
            if(user1 != null){
                map.put("code",CommonConstant.UNIFY_RETURN_PHONE_HAVE_BIND_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_HAVE_BIND_MSG);
                map.put("data","");
                return map;
            }
            String pwd = MyUtils.MD5(user.getUserPwd());
            // 手机获取验证码
            String rightCode = (String)redisUtils.get(user.getUserPhone());
            if(!redisUtils.hasKey(user.getUserPhone())){
                // 手机验证码失效
                map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_INVALID_MSG);
                map.put("data","");
                return map;
            }
            else if(rightCode.equals(phoneCode)){
                // 验证码正确 从redis中删除验证码
                redisUtils.del(user.getUserPhone());
                if(loginRegiservice.haveUserName(user.getUserName())){
                    // 用户名重复，重新输入
                    map.put("code",CommonConstant.UNIFY_RETURN_USERNAME_DUPLICATE_CODE);
                    map.put("msg",CommonConstant.UNIFY_RETURN_USERNAME_DUPLICATE_MSG);
                    map.put("data","");
                    return map;
                }
                user.setUserId(generateUserID());
                user.setUserPwd(pwd);
                loginRegiservice.saveUser(user);
                // 获取token并存入redis
                String token = loginRegiservice.getTokenByWX(user);
                map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_MSG);
                map.put("data",token);
                return map;
            }
            else{
                // 手机验证码错误
                map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_CODE);
                map.put("msg",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_MSG);
                map.put("data","");
                return map;
            }
        } catch (Exception exception) {
            map.put("code",CommonConstant.UNIFY_RETURN_PHONE_CODE_WRONG_CODE);
            map.put("msg","注册信息有误");
            map.put("data","");
            return map;
        }
    }

    /**
     * @author: HarryXzy
     * @date: 2022/6/10 13:26
     * @return:
     * @description: 退出登陆
     */
    @PostMapping("/logout")
    public Map<String, String> logout(@RequestParam("token")String token) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        redisUtils.del(token);
        map.put("code",CommonConstant.UNIFY_RETURN_SUCCESS_CODE);
        map.put("msg",CommonConstant.UNIFY_RETURN_SUCCESS_MSG);
        map.put("data","已退出登陆");
        return map;
    }




}
